#!/usr/bin/env bash
# Copyright (C) 2013 - 2018 Teddysun <i@teddysun.com>
# 
# This file is part of the LAMP script.
#
# LAMP is a powerful bash script for the installation of 
# Apache + PHP + MySQL/MariaDB/Percona and so on.
# You can install Apache + PHP + MySQL/MariaDB/Percona in an very easy way.
# Just need to input numbers to choose what you want to install before installation.
# And all things will be done in a few minutes.
#
# System Required:  CentOS 6+ / Debian 8+ / Ubuntu 14+
# Description:  Create, Delete, List Apache Virtual Host
# Website:  https://lamp.sh
# Github:   https://github.com/teddysun/lamp

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

apache_location=/usr/local/apache
mysql_location=/usr/local/mysql
mariadb_location=/usr/local/mariadb
percona_location=/usr/local/percona
web_root_dir=/data/www/default

rootness(){
    if [[ ${EUID} -ne 0 ]]; then
        echo "Error: This script must be run as root" 1>&2
        exit 1
    fi
}

vhost(){
    local action=$1
    case ${action} in
        add ) vhost_add;;
        list ) vhost_list;;
        del ) vhost_del;;
        *) echo "action ${action} not found";exit 1;;
    esac
}

db_name(){
    if [ -d ${mysql_location} ]; then
        echo "MySQL"
    elif [ -d ${mariadb_location} ]; then
        echo "MariaDB"
    elif [ -d ${percona_location} ]; then
        echo "Percona"
    else
        echo "MySQL"
    fi
}

set_apache_allow_syntax(){
    if [[ -s /usr/sbin/httpd ]]; then
        if /usr/sbin/httpd -v | grep -q "Apache/2.4"; then
            allow_from_all="Require all granted"
        else
            echo "Error: Can not get Apache version..."
            exit 1
        fi
    else
        echo "Error: Can not find Apache, may be not installed. Please check it and try again."
        exit 1
    fi
}

check_email(){
    regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
    if [[ ${1} =~ ${regex} ]]; then
        return 0
    else
        return 1
    fi
}

vhost_add(){
    set_apache_allow_syntax

    while :
    do
        read -p "Please enter server names (for example: www.lamp.sh lamp.sh): " server_names
        for i in ${server_names};do
            if apache_vhost_is_exist ${i};then
                echo "Error: virtual host [${i}] is exist, please check it and try again"
                break
            fi
            break 2
        done
    done

    default_root="/data/www/${server_names%% *}"
    read -p "Please enter website root directory(default:$default_root): " website_root
    website_root=${website_root:=$default_root}
    php_admin_value=""
    if [ -s /usr/bin/php ]; then
        php_admin_value="php_admin_value open_basedir ${website_root}:/tmp:/var/tmp:/proc"
        if [ -d "${web_root_dir}/phpmyadmin" ]; then
            php_admin_value="${php_admin_value}:${web_root_dir}/phpmyadmin"
        fi
        if [ -d "${web_root_dir}/kod" ]; then
            php_admin_value="${php_admin_value}:${web_root_dir}/kod"
        fi
    fi

    while :
    do
        read -p "Please enter Administrator Email address: " email
        if [ -z "${email}" ]; then
            echo "Error: Administrator Email address can not be empty"
        elif check_email ${email}; then
            echo "Administrator Email address:${email}"
            break
        else
            echo "Error: Please enter a correct email address"
        fi
    done

    while :
    do
        read -p "Do you want to create a database and mysql user with same name? [y/n]:" create
        case ${create} in
        y|Y)
            if [ ! "$(command -v "mysql")" ]; then
                echo "Error: $(db_name) is not installed, please check it and try again"
                exit 1
            fi
            mysql_count=$(ps -ef | grep -v grep | grep -c "mysqld")
            if [ ${mysql_count} -eq 0 ]; then
                echo "Info: $(db_name) looks like not running, Try to starting $(db_name)..."
                /etc/init.d/mysqld start > /dev/null 2>&1
                if [ $? -ne 0 ]; then
                    echo "Error: $(db_name) starting failed!"
                    exit 1
                fi
            fi
            read -p "Please enter your $(db_name) root password:" mysqlroot_passwd
            mysql -uroot -p${mysqlroot_passwd} <<EOF
exit
EOF
            if [ $? -ne 0 ]; then
                echo "Error: $(db_name) root password incorrect! Please check it and try again!"
                exit 1
            fi
            read -p "Please enter the database name:" dbname
            [ -z ${dbname} ] && echo "Error: database name can not be empty" && exit 1
            read -p "Please set the password for user ${dbname}:" mysqlpwd
            [ -z ${mysqlpwd} ] && echo "Error: user password can not be empty" && exit 1
            create="y"
            break
            ;;
        n|N)
            echo "Do not create a database"
            create="n"
            break
            ;;
        *) echo "Please enter only y or n"
        esac
    done

    mkdir -p /data/wwwlog/${server_names%% *} ${website_root}

    cat > ${apache_location}/conf/vhost/${server_names%% *}.conf << EOF
<VirtualHost *:80>
    ServerAdmin ${email}
    ${php_admin_value}
    ServerName ${server_names%% *}
    ServerAlias ${server_names}
    DocumentRoot ${website_root}
    <Directory ${website_root}>
        SetOutputFilter DEFLATE
        Options FollowSymLinks
        AllowOverride All
        Order Deny,Allow
        ${allow_from_all}
        DirectoryIndex index.php index.html index.htm
    </Directory>
    ErrorLog /data/wwwlog/${server_names%% *}/error.log
    CustomLog /data/wwwlog/${server_names%% *}/access.log combined
</VirtualHost>
EOF

    echo "Virtual host [${server_names%% *}] has been created"
    echo "Website root directory is: ${website_root}"

    if [ "$create" == "y" ]; then
        mysql -uroot -p${mysqlroot_passwd} <<EOF
CREATE DATABASE IF NOT EXISTS \`${dbname}\` CHARACTER SET utf8 COLLATE utf8_general_ci;
GRANT ALL PRIVILEGES ON \`${dbname}\` . * TO '${dbname}'@'localhost' IDENTIFIED BY '${mysqlpwd}';
GRANT ALL PRIVILEGES ON \`${dbname}\` . * TO '${dbname}'@'127.0.0.1' IDENTIFIED BY '${mysqlpwd}';
FLUSH PRIVILEGES;
EOF
        echo "Database [${dbname}] and mysql user [${dbname}] has been created"
    fi

    echo "Reloading the apache config file..."
    if ${apache_location}/bin/apachectl -t; then
        /etc/init.d/httpd restart
        echo "Reload success"
    else
        echo "Error: Reload failed! config file had an error, please fix it and try again"
        exit 1
    fi

    read -p "Do you want to add a SSL certificate? [y/n]:" create_ssl
    if [ "${create_ssl}" == "y" ] || [ "${create_ssl}" == "Y" ]; then
        add_ssl_memu
        add_ssl
        echo "Reloading the apache config file..."
        if ${apache_location}/bin/apachectl -t; then
            /etc/init.d/httpd restart
            echo "Reload success"
        else
            echo "Error: Reload failed! config file had an error, please fix it and try again"
        fi
    else
        echo "Do not add a SSL certificate"
    fi

    chown -R apache:apache /data/wwwlog/${server_names%% *} ${website_root}
    echo "All done"
}

add_ssl_memu(){
    echo -e "\033[32m1.\033[0m Use your own SSL Certificate and Key"
    echo -e "\033[32m2.\033[0m Use Let's Encrypt to create SSL Certificate and Key"
    while :
    do
        read -p "Please enter 1 or 2: " ssl_pick
        if [ "${ssl_pick}" == "1" ]; then
            while :
            do
            read -p "Please enter full path to SSL Certificate file: " ssl_certificate
            if [ -z "${ssl_certificate}" ]; then
                echo "Error: SSL Certificate file can not be empty"
            elif [ -f "${ssl_certificate}" ]; then
                break
            else
                echo "${ssl_certificate} does not exist or is not a file!"
            fi
            done

            while :
            do
            read -p "Please enter full path to SSL Certificate Key file: " ssl_certificate_key
            if [ -z "${ssl_certificate_key}" ]; then
                echo "Error: SSL Certificate Key file can not be empty"
            elif [ -f "${ssl_certificate_key}" ]; then
                break
            else
                echo "${ssl_certificate_key} does not exist or is not a file!"
            fi
            done
            break
        elif [ "${ssl_pick}" == "2" ]; then
            echo "You have already chosen Let's Encrypt"
            echo "It will be processed automatically"
            break
        else
            echo "Error: Please only enter 1 or 2"
        fi
    done

    read -p "Do you want force redirection from HTTP to HTTPS automatically? [y/n]:" force_ssl
}

create_ssl_config(){
    sed -i 's@#Include conf/extra/httpd-ssl.conf@Include conf/extra/httpd-ssl.conf@g' ${apache_location}/conf/httpd.conf
    cat >> ${apache_location}/conf/vhost/${server_names%% *}.conf << EOF
<VirtualHost *:443>
    ServerAdmin ${email}
    ${php_admin_value}
    DocumentRoot ${website_root}
    ServerName ${server_names%% *}
    ServerAlias ${server_names}
    SSLEngine on
    SSLCertificateFile ${ssl_certificate}
    SSLCertificateKeyFile ${ssl_certificate_key}
    <Directory ${website_root}>
        SetOutputFilter DEFLATE
        Options FollowSymLinks
        AllowOverride All
        Order Deny,Allow
        ${allow_from_all}
        DirectoryIndex index.php index.html index.htm
    </Directory>
    Header always set Strict-Transport-Security "max-age=31536000; preload"
    Header always edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options SAMEORIGIN
    ErrorLog  /data/wwwlog/${server_names%% *}/ssl_error.log
    CustomLog  /data/wwwlog/${server_names%% *}/ssl_access.log combined
</VirtualHost>
EOF
}

create_ssl_htaccess(){
    cat > ${website_root}/.htaccess << EOF
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
</IfModule>
EOF
}

add_ssl(){
    if [ "${ssl_pick}" == "1" ]; then
        create_ssl_config
    elif [ "${ssl_pick}" == "2" ]; then
        letsdomain=""
        if [ ! -z "${server_names}" ]; then
            for i in ${server_names}
            do
                letsdomain=${letsdomain}" -d ${i}"
            done
        fi
        add_letsencrypt
    fi
    if [ "${force_ssl}" == "y" ] || [ "${force_ssl}" == "Y" ]; then
        echo "You chosen force redirection from HTTP to HTTPS automatically"
        create_ssl_htaccess
        echo "and it's already done"
    else
        echo "Do not force redirection from HTTP to HTTPS automatically"
    fi
}

add_letsencrypt(){
    if [[ "${email}" == "" || "${website_root}" == "" || "${letsdomain}" == "" ]]; then
        echo "Error: [email, website_root, letsdomain] parameter are needed!"
        exit 1
    fi
    if [ ! -d "${website_root}" ]; then
        echo "Error: ${website_root} does not exist or is not a directory!"
        exit 1
    fi
    if [ ! -s /bin/certbot ]; then
        echo "Installing certbot command..."
        wget --no-check-certificate -O /bin/certbot https://dl.eff.org/certbot-auto
        chmod +x /bin/certbot
    fi

    echo "Starting create Let's Encrypt SSL Certificate..."
    /bin/certbot certonly --email ${email} --agree-tos -n --webroot -w ${website_root} ${letsdomain}
    if [ $? -eq 0 ]; then
        ssl_certificate="/etc/letsencrypt/live/${server_names%% *}/fullchain.pem"
        ssl_certificate_key="/etc/letsencrypt/live/${server_names%% *}/privkey.pem"
        create_ssl_config
        check_lets_cron
        echo "Create Let's Encrypt SSL Certificate successfully"
    else
        echo "Error: Create Let's Encrypt SSL Certificate failed!"
    fi
}

check_lets_cron(){
    if [ "$(command -v crontab)" ]; then
        if crontab -l | grep -q "/bin/certbot renew --disable-hook-validation"; then
            echo "Let's encrypt crontab renew rule is exist."
        else
            echo "Let's encrypt crontab renew rule is not exist, create it!"
            (crontab -l ; echo '0 3 */7 * * /bin/certbot renew --disable-hook-validation --renew-hook "/etc/init.d/httpd restart"') | crontab -
        fi
    fi
}

vhost_list(){
    if [ $(ls ${apache_location}/conf/vhost/ | grep ".conf$" | grep -v "none" | wc -l) -gt 0 ]; then
        echo "Server Name"
        echo "------------"
    else
        echo "Apache virtual host not found. You can create a new Apache virtual host with command: lamp add"
    fi
    ls ${apache_location}/conf/vhost/ | grep ".conf$" | grep -v "none" | sed 's/.conf//g'
}

vhost_del(){
    read -p "Please enter a domain you want to delete it (for example: www.lamp.sh): " domain
    if ! apache_vhost_is_exist "${domain}";then
        echo "Error: Virtual host [${domain}] not found!"
        exit 1
    else
        rm -f ${apache_location}/conf/vhost/${domain}.conf
        echo "Delete virtual host [${domain}] completed"
        echo "Website files will not be deleted for security reasons"
        echo "You need to manually delete the website files"

        echo "Reloading the apache config file..."
        if ${apache_location}/bin/apachectl -t;then
            /etc/init.d/httpd restart
            echo "Reload success"
        else
            echo "Error: Reload failed! config file had an error, please fix it and try again"
            exit 1
        fi
    fi
}

apache_vhost_is_exist(){
    local conf_file="${apache_location}/conf/vhost/$1.conf"
    if [ -f "${conf_file}" ]; then
        return 0
    else
        return 1
    fi
}

display_usage(){
printf "

Usage: `basename $0` [ add | del | list ]
add     Create a new Apache virtual host
del     Delete a Apache virtual host
list    List all of Apache virtual hosts

"
}

#Run it
rootness
if [ $# -ne 1 ]; then
    display_usage
    exit 1
fi

action=$1
case ${action} in
    add)  vhost ${action} ;;
    list) vhost ${action} ;;
    del)  vhost ${action} ;;
    *)    display_usage   ;;
esac
